package com.duojuhe.coremodule.quartz.service.impl;

import com.duojuhe.cache.SystemDictCache;
import com.duojuhe.common.annotation.DataScopeFilter;
import com.duojuhe.common.annotation.KeyLock;
import com.duojuhe.common.bean.UserTokenInfoVo;
import com.duojuhe.coremodule.quartz.enums.QuartzEnum;
import com.duojuhe.common.enums.SystemEnum;
import com.duojuhe.common.exception.base.DuoJuHeException;
import com.duojuhe.common.result.ErrorCodes;
import com.duojuhe.common.result.PageResult;
import com.duojuhe.common.result.ServiceResult;
import com.duojuhe.common.utils.cronutil.CronExpressionUtil;
import com.duojuhe.common.utils.idgenerator.UUIDUtils;
import com.duojuhe.common.utils.page.PageHelperUtil;
import com.duojuhe.common.utils.stringutil.StringUtil;
import com.duojuhe.coremodule.BaseService;
import com.duojuhe.coremodule.quartz.entity.QuartzJob;
import com.duojuhe.coremodule.quartz.mapper.QuartzJobMapper;
import com.duojuhe.coremodule.quartz.pojo.dto.job.*;
import com.duojuhe.coremodule.quartz.service.QuartzJobService;
import com.duojuhe.coremodule.quartz.util.ScheduleUtils;
import com.duojuhe.coremodule.system.entity.SystemDict;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.quartz.Scheduler;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.PostConstruct;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
public class QuartzJobServiceImpl  extends BaseService implements QuartzJobService {
    @Resource
    private SystemDictCache systemDictCache;
    @Resource
    private Scheduler scheduler;
    @Resource
    private QuartzJobMapper quartzJobMapper;

    /**
     * 项目启动时，初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理（注：不能手动修改数据库ID和任务组名，否则会导致脏数据）
     */
    @PostConstruct
    public void initQuartzJob(){
        log.info("=====初始化定时器开始=====");
       try {
           scheduler.clear();
           List<QuartzJob> jobList = quartzJobMapper.selectAll();
           for (QuartzJob quartzJob : jobList) {
               ScheduleUtils.createScheduleJob(scheduler, quartzJob);
           }
       }catch (Exception e){
           log.error("初始化定时任务出现异常", e);
       }
        log.info("=====初始化定时器结束=====");
    }

    /**
     * 【分页查询】根据条件查询定时任务list
     * @param req
     * @return
     */
    @DataScopeFilter
    @Override
    public ServiceResult<PageResult<List<QueryQuartzJobPageRes>>> queryQuartzJobResList(QueryQuartzJobPageReq req) {
        PageHelperUtil.orderByAndStartPage(req, "createTime desc, jobId desc");
        List<QueryQuartzJobPageRes> list = quartzJobMapper.queryQuartzJobResList(req);
        list.forEach(this::setHandleQuartzJobDictNameColor);
        return PageHelperUtil.returnServiceResult(req, list);
    }


    /**
     * 【保存】调度作业
     * @param req
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ServiceResult saveQuartzJob(SaveQuartzJobReq req) {
        if(!CronExpressionUtil.isValidExpression(req.getCronExpression())){
            return ServiceResult.fail(ErrorCodes.CRON_EXPRESSION_ERROR);
        }
        //检查调度任务名称
        checkExistJobName(req.getJobName());
        //检查作业调度任务路径
        checkJobClassPath(req.getClassPath());
        //调度作业id
        String jobId = UUIDUtils.getUUID32();
        //检查方法是否加入过系统中
        checkExistClassPathMethodName(jobId,req.getClassPath(),req.getMethodName());
        //当前登录用户
        UserTokenInfoVo userTokenInfoVo = getCurrentLoginUserInfoDTO();
        //租户id
        String tenantId = userTokenInfoVo.getTenantId();
        //用户id
        String userId = userTokenInfoVo.getUserId();
        //当前时间
        Date nowDate = new Date();
        QuartzJob quartzJob = new QuartzJob();
        quartzJob.setJobId(jobId);
        quartzJob.setJobName(req.getJobName());
        quartzJob.setGroupId(jobId);
        quartzJob.setCreateTime(nowDate);
        quartzJob.setCreateUserId(userId);
        quartzJob.setUpdateTime(nowDate);
        quartzJob.setUpdateUserId(userId);
        quartzJob.setCreateDeptId(userTokenInfoVo.getCreateDeptId());
        quartzJob.setTenantId(tenantId);
        quartzJob.setClassPath(req.getClassPath());
        quartzJob.setBuiltIn(SystemEnum.YES_NO.NO.getKey());
        quartzJob.setIsSyncCode(req.getIsSyncCode());
        quartzJob.setSpringId(req.getSpringId());
        quartzJob.setMethodName(req.getMethodName());
        quartzJob.setCronExpression(req.getCronExpression());
        quartzJob.setDescription(req.getDescription());
        quartzJob.setJobStatusCode(QuartzEnum.QUARTZ_STATUS.NOT_RUNNING.getKey());
        int rows = quartzJobMapper.insertSelective(quartzJob);
        if (rows > 0) {
            ScheduleUtils.createScheduleJob(scheduler,quartzJob);
        }
        return ServiceResult.ok(jobId);
    }


    /**
     * 【修改】调度作业
     * @param req
     * @return
     */
    @KeyLock(lockKeyParts = "jobId")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ServiceResult updateQuartzJob(UpdateQuartzJobReq req) {
        QuartzJob quartzJob = quartzJobMapper.selectByPrimaryKey(req.getJobId());
        if (quartzJob == null) {
            return ServiceResult.fail(ErrorCodes.PARAM_ERROR);
        }
        if(SystemEnum.YES_NO.YES.getKey().equals(quartzJob.getBuiltIn())){
            return ServiceResult.fail(ErrorCodes.QUARTZ_JOB_NOT_UPDATE);
        }
        if(!CronExpressionUtil.isValidExpression(req.getCronExpression())){
            return ServiceResult.fail(ErrorCodes.CRON_EXPRESSION_ERROR);
        }
        //检查调度任务名称
        if (!req.getJobName().equals(quartzJob.getJobName())){
            checkExistJobName(req.getJobName());
        }
        //检查作业调度任务路径
        checkJobClassPath(req.getClassPath());
        //调度作业id
        String jobId = quartzJob.getJobId();
        //检查方法是否加入过系统中
        checkExistClassPathMethodName(jobId,req.getClassPath(),req.getMethodName());
        //当前登录用户
        UserTokenInfoVo userTokenInfoVo = getCurrentLoginUserInfoDTO();
        //用户id
        String userId = userTokenInfoVo.getUserId();
        //当前时间
        Date nowDate = new Date();
        quartzJob.setJobName(req.getJobName());
        quartzJob.setUpdateTime(nowDate);
        quartzJob.setUpdateUserId(userId);
        quartzJob.setClassPath(req.getClassPath());
        quartzJob.setIsSyncCode(req.getIsSyncCode());
        quartzJob.setSpringId(req.getSpringId());
        quartzJob.setMethodName(req.getMethodName());
        quartzJob.setCronExpression(req.getCronExpression());
        quartzJob.setDescription(req.getDescription());
        //以下参数个不更新数据库
        quartzJob.setLastTime(null);
        quartzJob.setNextTime(null);
        quartzJob.setRemark(null);
        int rows = quartzJobMapper.updateByPrimaryKeySelective(quartzJob);
        if (rows > 0) {
            ScheduleUtils.updateScheduleJob(scheduler,quartzJob);
        }
        return ServiceResult.ok(req.getJobId());
    }

    /**
     * 【暂停调度任务】根据作业id暂停调度任务
     * @param req
     * @return
     */
    @KeyLock(lockKeyParts = "jobId")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ServiceResult pauseQuartzJobByJobId(QuartzJobIdReq req) {
        //任务id
        String jobId = req.getJobId();
        QuartzJob quartzJobOld = quartzJobMapper.selectByPrimaryKey(jobId);
        if (quartzJobOld == null) {
            return ServiceResult.fail(ErrorCodes.PARAM_ERROR);
        }
        if(SystemEnum.YES_NO.YES.getKey().equals(quartzJobOld.getBuiltIn())){
            return ServiceResult.fail(ErrorCodes.PARAM_ERROR);
        }
        if(QuartzEnum.QUARTZ_STATUS.NOT_RUNNING.getKey().equals(quartzJobOld.getJobStatusCode())){
            return ServiceResult.fail("该任务已暂停，请勿重复暂停!");
        }
        QuartzJob quartzJob = new QuartzJob();
        quartzJob.setJobId(jobId);
        quartzJob.setJobStatusCode(QuartzEnum.QUARTZ_STATUS.NOT_RUNNING.getKey());
        int rows = quartzJobMapper.updateByPrimaryKeySelective(quartzJob);
        if (rows>0){
            ScheduleUtils.pauseJob(scheduler, jobId, quartzJobOld.getGroupId());
        }
        return ServiceResult.ok(jobId);
    }

    /**
     * 【恢复调度任务】根据作业id恢复调度任务
     * @param req
     * @return
     */
    @KeyLock(lockKeyParts = "jobId")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ServiceResult resumeQuartzJobByJobId(QuartzJobIdReq req) {
        //任务id
        String jobId = req.getJobId();
        QuartzJob quartzJobOld = quartzJobMapper.selectByPrimaryKey(jobId);
        if (quartzJobOld == null) {
            return ServiceResult.fail(ErrorCodes.PARAM_ERROR);
        }
        //判断所选任务中是否存在内置任务
        if (SystemEnum.YES_NO.YES.getKey().equals(quartzJobOld.getBuiltIn())) {
            return ServiceResult.fail(ErrorCodes.PARAM_ERROR);
        }
        //任务状态
        String running = QuartzEnum.QUARTZ_STATUS.RUNNING.getKey();
        if(running.equals(quartzJobOld.getJobStatusCode())){
            return ServiceResult.fail("该任务已运行，请勿重复运行!");
        }
        quartzJobOld.setJobStatusCode(running);
        QuartzJob quartzJob = new QuartzJob();
        quartzJob.setJobId(jobId);
        quartzJob.setJobStatusCode(running);
        int rows = quartzJobMapper.updateByPrimaryKeySelective(quartzJob);
        if (rows>0){
            ScheduleUtils.resumeOrCreateJob(scheduler, quartzJobOld);
        }
        return ServiceResult.ok(jobId);
    }

    /**
     * 删除调度任务
     *
     * @return
     */
    @KeyLock(lockKeyParts = "jobId")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ServiceResult deleteQuartzJobByJobId(QuartzJobIdReq req) {
        String jobId = req.getJobId();
        QuartzJob quartzJob = quartzJobMapper.selectByPrimaryKey(jobId);
        if (quartzJob == null) {
            return ServiceResult.fail(ErrorCodes.PARAM_ERROR);
        }
        //判断所选任务中是否存在内置任务
        if (SystemEnum.YES_NO.YES.getKey().equals(quartzJob.getBuiltIn())) {
            return ServiceResult.fail(ErrorCodes.QUARTZ_JOB_NOT_DELETE);
        }
        int rows = quartzJobMapper.deleteByPrimaryKey(jobId);
        if (rows > 0) {
            ScheduleUtils.deleteJob(scheduler,jobId, quartzJob.getGroupId());
        }
        return ServiceResult.ok(jobId);
    }

    /**
     * 【执行一次】根据作业id执行一次
     * @param req
     * @return
     */
    @KeyLock(lockKeyParts = "jobId")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ServiceResult runOnceJobByJobId(QuartzJobIdReq req) {
        String jobId = req.getJobId();
        QuartzJob quartzJob = quartzJobMapper.selectByPrimaryKey(jobId);
        if (quartzJob == null) {
            return ServiceResult.fail(ErrorCodes.PARAM_ERROR);
        }
        //判断所选任务中是否存在内置任务
        if (SystemEnum.YES_NO.YES.getKey().equals(quartzJob.getBuiltIn())) {
            return ServiceResult.fail(ErrorCodes.PARAM_ERROR);
        }
        if (!QuartzEnum.QUARTZ_STATUS.RUNNING.getKey().equals(quartzJob.getJobStatusCode())) {
            return ServiceResult.fail("任务不在运行状态，禁止该操作!");
        }
        ScheduleUtils.runOnceJob(scheduler,jobId, quartzJob.getGroupId());
        return ServiceResult.ok(jobId);
    }



    /*====================================私有方法开始================================================**/

    /**
     * 检查任务名称是否存在
     */
    private void checkExistJobName(String jobName) {
        if (StringUtils.isBlank(jobName)){
            throw new DuoJuHeException(ErrorCodes.QUARTZ_JOB_NAME_EXIST);
        }
        Example example = new Example(QuartzJob.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("jobName", jobName);
        List<QuartzJob> list = quartzJobMapper.selectByExample(example);
        if (list.size() > 0) {
            throw new DuoJuHeException(ErrorCodes.QUARTZ_JOB_NAME_EXIST);
        }
    }


    /**
     * 检查定时任务执行报名路径
     * @param classPath
     */
    private void checkJobClassPath(String classPath){
        if (!StringUtil.containsAnyIgnoreCase(classPath, ScheduleUtils.JOB_WHITELIST_STR)){
            throw new DuoJuHeException(ErrorCodes.QUARTZ_JOB_CLASS_PATH_NO_WHITELIST);
        }
    }

    /**
     * 检查任务路径
     */
    private void checkExistClassPathMethodName(String jobId,String classPath,String methodName) {
        Example exampleOne = new Example(QuartzJob.class);
        Example.Criteria criteriaOne = exampleOne.createCriteria();
        criteriaOne.andEqualTo("methodName", methodName);
        criteriaOne.andEqualTo("classPath", classPath);
        criteriaOne.andNotEqualTo("jobId", jobId);
        List<QuartzJob> listOne =  quartzJobMapper.selectByExample(exampleOne);
        if(listOne.size() > 0){
            throw new DuoJuHeException(ErrorCodes.QUARTZ_JOB_CLASS_PATH_METHOD_NAME_EXIST);
        }
    }

    /**
     * 处理返回值的数据字典
     *
     * @param res
     */
    private void setHandleQuartzJobDictNameColor(HandleQuartzJobDictNameColorRes res) {
        if (res == null) {
            return;
        }
        SystemDict dictBuiltIn = systemDictCache.getSystemDictByDictCode(res.getBuiltIn());
        res.setBuiltInName(dictBuiltIn.getDictName());
        res.setBuiltInColor(dictBuiltIn.getDictColor());

        SystemDict isSyncCode = systemDictCache.getSystemDictByDictCode(res.getIsSyncCode());
        res.setIsSyncName(isSyncCode.getDictName());
        res.setIsSyncColor(isSyncCode.getDictColor());

        SystemDict jobStatusCode = systemDictCache.getSystemDictByDictCode(res.getJobStatusCode());
        res.setJobStatusName(jobStatusCode.getDictName());
        res.setJobStatusColor(jobStatusCode.getDictColor());
    }
    /*====================================私有方法end================================================**/
}
